Skip to content

feat(chat): open message links on the linked message - #1055

Merged
bmc08gt merged 8 commits into
mainfrom
feat/message-link-route
Oct 16, 2026
Merged

bmc08gt merged 8 commits into
mainfrom
feat/message-link-route

Conversation

@bmc08gt

@bmc08gt bmc08gt commented Oct 14, 2026

Copy link
Copy Markdown
Collaborator

https://app.flipcash.com/chat/{chatId}/m/{seq} opened the chat at the bottom: Route parsed it as .chat(id) and dropped the message. This is task A of the message-links design (route and land); the message card follows in a stacked PR.

  • Route parses /m/{seq} to .chatMessage(id, MessageID(value: seq)). A non-numeric seq falls back to .chat(id), as before.
  • DeepLinkAction routes it through routeChat to a new .chatMessage(id, MessageLanding) destination, the same ConversationScreen with a landing. A private group the viewer isn't in still opens its profile.
  • Landing reuses the reply jump (scrollToMessage → flashAttention). When the message isn't stored, MessageLoader.revealPaging pages older history back from the oldest stored message until it is, up to 20 pages. Paging back keeps the window contiguous; a page fetched around the target would leave a hole, because the window reads forward from its anchor.
  • A link to the chat that's already open jumps in place rather than remounting it.
  • A link tapped in another chat shows a back chip under the bar (36pt, radius 18, white 12% fill; node 11493:212). It returns to the source chat and hides when the user drags the transcript or sends.
  • The scan allowlist accepts the new route. LinkCardClassifier sends it to the group card until the message card lands.

On the simulator, flipcash://chat/<Jeffy group>/m/150 with messages 315–364 stored paged back to 115 and landed on 150; a second link to 62, while that chat was open, paged back to 15 and landed on 62.

Known limits:

  • A message more than 20 pages back gets the "Message Unavailable" dialog, which says it may have been deleted.
  • loadOlderMessages returns early when canRead is false, so a viewer whose transcript is obscured by the gate can only land inside the initial window.

`/chat/{id}/m/{seq}` parsed as `.chat(id)` and dropped the message, so a
message link opened the chat at the bottom. It now parses to
`.chatMessage(id, seq)` and routes through `routeChat` to a sibling
destination that carries the message.

The transcript lands with the reply jump's scroll-and-flash. When the
message isn't stored locally, `MessageLoader.revealPaging` pages older
history back from the oldest stored message until it is, capped at 20
pages, so the window stays contiguous. A link to the open chat jumps in
place instead of remounting it.

A link tapped in another chat shows a back chip under the bar that
returns there. It hides when the user drags the transcript or sends.

A malformed `/m/` segment falls back to `.chat(id)`. A private group the
viewer isn't in still opens its profile. The scan allowlist and the
link-card classifier accept the new route; the classifier keeps the
group card until the message card lands.
A link to a message older than the first page landed with the target as
the window's first row, half under the top edge, because `reveal`
anchored the window on the target itself. It now anchors 12 messages
earlier where history allows, and `revealPaging` fetches one more page
when the page it stopped on begins at the target.

`MessageLinkSmokeTests` opens `/chat/{id}/m/100` in the XCTests tip DM,
whose first page holds 251-300, and waits for the highlighted row to be
on screen. The row a jump lands on carries the `chat-attention-target`
accessibility identifier while the flash runs, so the test can find it.
* commit '16a24ad1eb3eeac2097eac72d32150ba2c368f18':
  fix(ui): draw UIKit caption in Avenir Medium like SwiftUI (#1064)
  fix(members): host chip and member count label to match Android (#1058)
  chore(chat): log the attach lifecycle the same way in both hosts (#1062)
  refactor(chat): decide attach close events in one place (#1061)
  refactor(chat): measure the attach card's frame once (#1060)
  refactor(chat): read the attach menu rows live over the keyboard (#1059)
  refactor(chat): build the attach actions once for both hosts (#1056)
  fix(chat): render system messages in the transcript (#1054)

# Conflicts:
#	Flipcash/Core/Screens/Conversation/ChatScreenRepresentable.swift
It used a flat 12% white fill. On iOS 26 it now takes the same interactive
glass as the composer; older iOS keeps the fill and hairline border.
…chat closes

Dragging the transcript or sending a message no longer hides the chip,
matching Android. The drag and own-message hooks existed only for the chip,
so they go with it.
A message link tapped in a chat replaced the whole chat stack, so the back
chip reopened the source chat fresh at the bottom. The landed chat is now
pushed over the source when that chat is the visible screen, and the chip
pops back to it. A link that can't go on top (another tab or a sheet is up)
still navigates, and the chip still reopens the chat.
@bmc08gt
bmc08gt merged commit 4a843b6 into main Oct 16, 2026
3 checks passed
bmc08gt added a commit that referenced this pull request Oct 16, 2026
* origin/main: (23 commits)
  feat(chat): open message links on the linked message (#1055)
  fix(chat): load the link preview for a draft restored on opening a chat
  fix(chat): bring a removed link preview back once its link is edited out of the draft
  fix(chat): animate the composer link card in and out on the chip spring
  fix(chat): draw the composer link preview as a compact card with its remove button on the corner
  fix(chat): enlarge the composer link image and pin its remove button to it
  feat(chat): send a link preview alongside staged photos
  feat(chat): show the link preview's page in the composer before its image uploads
  feat(chat): draw a web card from the sender's preview and drop the receive-side fetch
  feat(chat): build a link preview in the composer and send it with the text
  feat(chat): carry a text message's link preview on the wire and in the store
  feat(chat): add the sender-side link preview checks and their shared vectors
  fix(chat): match Android's update-app bubble border and subtitle
  fix(chat): stand the jump button outside the bar clip so the reply strip no longer leaks (#1074)
  build: update release manifest
  fix(home): draw unselected tab glyphs and labels white on every iOS version (#1073)
  feat(settings): version card for TestFlight builds, kept current by the release manifest (#1072)
  feat(settings): add the update dot and version card (#1070)
  feat(update): tell users when a newer App Store version is out (#1069)
  feat(chat): jump-to-bottom button with unread badge (#1068)
  ...

# Conflicts:
#	Flipcash/Core/Screens/Conversation/LinkCardClassifier.swift
#	Flipcash/Core/Screens/Conversation/LinkCardFeed.swift
#	Flipcash/Core/Screens/Conversation/LinkCardResolver.swift
#	Flipcash/Core/Session/SessionAuthenticator.swift
#	FlipcashCore/Sources/FlipcashCore/Models/Chat/LinkCard.swift
#	FlipcashTests/Chat/LinkCardResolverTests.swift
#	FlipcashUI/Sources/FlipcashUI/Chat/ChatShareProfileCell.swift
#	FlipcashUI/Sources/FlipcashUI/Chat/LinkCardSource.swift
#	FlipcashUI/Sources/FlipcashUI/Chat/LinkCardView.swift
bmc08gt added a commit that referenced this pull request Oct 16, 2026
…t/message-link-share

* origin/feat/message-link-card: (23 commits)
  feat(chat): open message links on the linked message (#1055)
  fix(chat): load the link preview for a draft restored on opening a chat
  fix(chat): bring a removed link preview back once its link is edited out of the draft
  fix(chat): animate the composer link card in and out on the chip spring
  fix(chat): draw the composer link preview as a compact card with its remove button on the corner
  fix(chat): enlarge the composer link image and pin its remove button to it
  feat(chat): send a link preview alongside staged photos
  feat(chat): show the link preview's page in the composer before its image uploads
  feat(chat): draw a web card from the sender's preview and drop the receive-side fetch
  feat(chat): build a link preview in the composer and send it with the text
  feat(chat): carry a text message's link preview on the wire and in the store
  feat(chat): add the sender-side link preview checks and their shared vectors
  fix(chat): match Android's update-app bubble border and subtitle
  fix(chat): stand the jump button outside the bar clip so the reply strip no longer leaks (#1074)
  build: update release manifest
  fix(home): draw unselected tab glyphs and labels white on every iOS version (#1073)
  feat(settings): version card for TestFlight builds, kept current by the release manifest (#1072)
  feat(settings): add the update dot and version card (#1070)
  feat(update): tell users when a newer App Store version is out (#1069)
  feat(chat): jump-to-bottom button with unread badge (#1068)
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant